﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.AddIn.Hosting;
using RootfusCoreComponents;
using RootfusCoreComponents.IO;
using RootfusHostView;
using RoofusUIControls;

namespace Rootfus
{
    /// <summary>
    /// The main window for the Rootfus application (ROOt surFUS - short for surface).
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            LoadAddIns();
            ConfigureCommands();
        }

        /// <summary>
        /// Setup command bindings
        /// </summary>
        private void ConfigureCommands()
        {
            CommandBindings.Add(new CommandBinding(
                ApplicationCommands.Save,
                (sender, e) => MenuItemSave_Click(sender, e)));
        }

        /// <summary>
        /// Define the dependency property that will expose the various add-ins we can go after.
        /// </summary>
        static public readonly DependencyProperty SurfaceItemAddinTokensProperty = DependencyProperty.Register(
            "SurfaceItemAddinTokens",
            typeof(IList<AddInToken>),
            typeof(MainWindow));

        public IList<AddInToken> SurfaceItemAddinTokens
        {
            get { return (IList<AddInToken>)GetValue(SurfaceItemAddinTokensProperty); }
            set { SetValue(SurfaceItemAddinTokensProperty, value); }
        }

        /// <summary>
        /// Find all the addins that we can use, and load them up!
        /// </summary>
        private void LoadAddIns()
        {
            ///
            /// Load all add-ins tokens we can find. This should not actually activate any of them (I hope).
            /// Some directories are required: http://msdn2.microsoft.com/en-us/library/bb384240(VS.90).aspx
            /// The relative path is here for now to enable debugging. It is ugly, but don't know how to properly
            /// specify start option in a directory independent way in the startup options project config.
            /// 

            string path = @"...\..\..\AddinRoot";
            string[] results = AddInStore.Update(path);
            if (results.Length > 0)
            {
                throw new Exception("Addins failed to load!");
            }
            SurfaceItemAddinTokens = AddInStore.FindAddIns(typeof(RootfusSurfaceItem), path);
        }

        /// <summary>
        /// The window is closing. Could have been the Quit command or perhaps they just clicked
        /// on the "close" box. Either way, we need to check to see if they are going to loose
        /// changes.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            if (_surface.Modified && !e.Cancel)
            {
                e.Cancel = true;
                MessageBoxResult result = MessageBox.Show("You haven't saved this ROOT Surface -- save first?", "Save everything?", MessageBoxButton.YesNoCancel);
                switch (result)
                {
                    case MessageBoxResult.Cancel:
                        return;
                    case MessageBoxResult.No:
                        e.Cancel = false;
                        break;
                    case MessageBoxResult.Yes:
                        if (DoSave())
                        {
                            e.Cancel = false;
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// The guy wants to quit us! Wow! The nerve! :-) Make sure that nothing is still modified!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemQuit_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Keep track of where we are creating all of our app-domains.
        /// </summary>
        private AppDomain _addin_appdomain = null;

        /// <summary>
        /// The user wants to add soemthing to the surface. run it.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _insert_menu_Click(object sender, RoutedEventArgs e)
        {
            ///
            /// Get the addin token and activate it. We assume that there are no security issues.
            /// This is probably bad given we are loading native code (ROOT) and we might want to unload it.
            /// Would be nice to put it all in a seperate process (which we can do with this system).
            /// 

            AddInToken token = (e.OriginalSource as MenuItem).Header as AddInToken;
            RootfusSurfaceItem rsi = ActivateRootfusSurfaceItemAddIn(token);

            ///
            /// Wrap it so we can track it internally, and then place it on our surface.
            /// 

            RootfusSurfaceItemWrapper wrap = new RootfusSurfaceItemWrapper(rsi, token.Name, token.Name);
            _surface.AddItem(wrap);
        }

        /// <summary>
        /// Does the actual work of activating an AddIn -- returns it for later use.
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private RootfusSurfaceItem ActivateRootfusSurfaceItemAddIn(AddInToken token)
        {
            RootfusSurfaceItem rsi;
            if (_addin_appdomain == null)
            {
                rsi = token.Activate<RootfusSurfaceItem>(AddInSecurityLevel.FullTrust, "Rootfus Addin AppDomain");
                AddInController cntrl = AddInController.GetAddInController(rsi);
                _addin_appdomain = cntrl.AppDomain;
            }
            else
            {
                rsi = token.Activate<RootfusSurfaceItem>(_addin_appdomain);
            }
            return rsi;
        }

        /// <summary>
        /// Holds onto the name of the file we are editing right now.
        /// </summary>
        private string _save_filename = null;

        private void MenuItemSaveAs_Click(object sender, RoutedEventArgs e)
        {
            DoSaveAs();
        }

        private bool DoSaveAs()
        {
            ///
            /// Get a new filename
            /// 

            Microsoft.Win32.SaveFileDialog sd = new Microsoft.Win32.SaveFileDialog();
            sd.AddExtension = true;
            sd.DefaultExt = ".rootfus";
            sd.FileName = _save_filename;
            sd.Filter = "Rootfus Files|*.rootfus|All files|*.*";
            if (_save_filename == null)
            {
                sd.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            }
            sd.OverwritePrompt = true;
            sd.Title = "Save Rootfus document as...";
            sd.ValidateNames = true;

            if (sd.ShowDialog().Value)
            {
                _save_filename = sd.FileName;
            }
            else
            {
                return false;
            }

            ///
            /// Finally, we can save the thing!
            /// 

            _surface.SaveToFile(_save_filename);
            _surface.Modified = false;
            return true;
        }

        /// <summary>
        /// Save the current surface to a file so they can get on with life!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemSave_Click(object sender, RoutedEventArgs e)
        {
            DoSave();
        }

        private bool DoSave()
        {
            if (_save_filename == null)
            {
                return DoSaveAs();
            }
            else
            {
                _surface.SaveToFile(_save_filename);
                _surface.Modified = false;
                return true;
            }
        }

        /// <summary>
        /// User wants to clear out the document we are looking at. Kill it.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemNew_Click(object sender, RoutedEventArgs e)
        {
            AskUserToClearWork();
        }

        /// <summary>
        /// See if the user really wants to destroy everything up there. Oh well! :-)
        /// </summary>
        /// <returns></returns>
        private bool AskUserToClearWork()
        {
            if (_surface.Modified)
            {
                if (MessageBox.Show("You haven't saved this ROOT Surface -- are you sure you want to erase everything?", "Erase everythign?", MessageBoxButton.YesNo) == MessageBoxResult.No)
                {
                    return false;
                }
            }

            ///
            /// Ok -- kill it off!
            /// 

            _surface.RemoveAllSIItems();
            _save_filename = "";
            _surface.Modified = false;
            return true;
        }

        /// <summary>
        /// User would like to open a file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemOpen_Click(object sender, RoutedEventArgs e)
        {
            ///
            /// The user needs to commit...
            ///

            if (!AskUserToClearWork())
            {
                return;
            }

            ///
            /// Get the file we are going to open.
            /// 

            Microsoft.Win32.OpenFileDialog sd = new Microsoft.Win32.OpenFileDialog();
            sd.AddExtension = true;
            sd.DefaultExt = ".rootfus";
            sd.FileName = "";
            sd.Filter = "Rootfus Files|*.rootfus|All files|*.*";
            sd.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            sd.Title = "Open Rootfus document...";

            if (sd.ShowDialog().Value)
            {
                _save_filename = sd.FileName;

                ///
                /// Now the ugly code -- we have to do it at the top level because that is where the AddIn's are created..
                /// 

                ItemFileOperator loader = new ItemFileOperator(_save_filename);
                loader.Load(
                    delegate(string addinname, string name, Point pos, Size sz)
                    {
                        /// Create the addin and place it. Get the addin token.
                        AddInToken token = SurfaceItemAddinTokens.First(x => x.Name == addinname);
                        if (token == null)
                        {
                            return null;
                        }
                        RootfusSurfaceItem item = ActivateRootfusSurfaceItemAddIn(token);
                        RootfusSurfaceItemWrapper wrapper = new RootfusSurfaceItemWrapper(item, name, addinname);
                        wrapper.UserName = name;
                        SurfaceItem surface_item = _surface.AddItem(wrapper, pos);
                        if (item.SurfaceUIResizeAllowed)
                        {
                            surface_item.ItemSize = sz;
                        }

                        return wrapper;
                    },
                    delegate(string source_name, string sink_name)
                    {
                        _surface.AddGUIItemConnection(source_name, sink_name);
                    }
                    );

                ///
                /// Everything loaded. Of course, we need to make sure that the surface isn't marked
                /// as modified (which it will have been by the process of adding the above items).
                /// 

                _surface.Modified = false;
            }
        }
    }
}
